<?php

namespace Helper;

//<System Mode>,<Operation Mode>[,<MCC>-<MNC>,<
//TAC>,<SCellID>,<PCellID>,<Frequency Band>,<earfcn>,<dlb
//w>,<ulbw>,<RSRQ>,<RSRP>,<RSSI>,<RSSNR>]

// 传过来的字符串
/*$string = '<System Mode>,<Operation Mode>[,<MCC>-<MNC>,<
TAC>,<SCellID>,<PCellID>,<Frequency Band>,<earfcn>,<dlb
w>,<ulbw>,<RSRQ>,<RSRP>,<RSSI>,<RSSNR>]';
*/

use Core\Model;
use Core\TbRedis;

/**
 * 用来解析基站数据
 * 
 * 使用方式
 * $str = 'LTE,Online,460-01,0x252A,114904065,398,EUTRAN-BAND1,450,5,5,-44,-970,-733,17';
 *  
 * var_dump(LBS::parse($str));
 *
 * MCC
 * MNC
 * LAC/TAC
 * Cell ID
 *
 * @author Lvzh
 */
class LBS
{
    /**
     * 对应详细的长度，当长度不满足时，不进行任何解析
     */
	private const GSM_DATA_LEN = 9;
	private const WCDMA_DATA_LEN = 14;
	private const TDSCDMA_DATA_LEN = 8;
	private const LTE_DATA_LEN = 14;
	private const CDMA_DATA_LEN = 14;
	private const EVDO_DATA_LEN = 11;
	//private const LXLTE_DATA_LEN = ;

	private static $instance = null;
	private static $table = 'lbs_address';

	// Mobile Country Code 移动国家代码
	private const MCC = [
		'460' => '中国',
		'454' => '香港',
		'455' => '澳门',
		'466' => '台湾',
	];

	// Mobile Network Code 移动网络代码
	private const MNC = [
		'00' => '移动',
		'02' => '移动',
		'07' => '移动',
		'01' => '联通',
		'06' => '联通',
		'03' => '电信',
		'05' => '电信',
		'11' => '电信',
		'20' => '铁通',
	];

	private function __construct() {}

    /**
     *
     *
     * @param $string
     * @return array|bool
     */
	public static function parse($string)
	{
		if (!self::$instance) self::$instance = new self();

		$arr = explode(',', trim($string));

		$systemMode = self::filterSystemMode($arr[0]);


		// 当不存在对应的mode时
        if (empty($systemMode)) {
            return false;
        }

		// 方法2, 判断方法是否存在
		$method = 'parse' . $systemMode;
        if (!method_exists(self::$instance, $method)) {
            return false;
        }

		$minLen = constant('self::' . $systemMode . '_DATA_LEN');

		// 长度不合法(数据不完整)
		if (count($arr) < $minLen) return false;

		// 解析失败
		if (!$parseData = self::$instance->$method($arr)) {
			return false;
		}

        /**
         * 如果数据库中存在对应基站的记录，则直接返回
         * 如果没有记录，则从LBS接口中获取
         * 获取成功后保存到数据库
         */
		if ($data = self::getAddressViaDb($parseData)) return $data;

        $address = self::getAddressByLBS($parseData);

        if ($address['errcode']) return false;

        unset($address['errcode']);

		$perfectData =  $address + self::improveParseData($parseData);

        self::saveAddressData($perfectData);

        return $perfectData;
	}

    /**
     * 完善数据
     * LAC(TAC)>=40960 -> 3G基站
     * LAC(TAC)<40960 并且 CELLID(ECI) > 65535 -> 4G基站
     * LAC(TAC)<40960 并且 CELLID(ECI) <= 65535 -> 2G基站
     * 将mnc, mcc代号转化为对应的国家和运营商
     * 计算出基站类型2G, 3G, 4G
     * @param $parseData
     * @return mixed
     */
	public static function improveParseData($parseData)
	{
		$parseData['mnc_parse'] = self::MNC[$parseData['mnc']]  ?? '电信';
		$parseData['mcc_parse'] = self::MCC[$parseData['mcc']];

		if ($parseData['lac'] >= 40960) {
			$parseData['type'] = '3G';
		} else {
			$parseData['type'] = $parseData['ci'] > 65535 ? '4G' : '2G';
		}

		return $parseData;
	}

    /**
     * 从数据中获取数据
     * @param $data
     * @return array|bool
     */
	protected static function getAddressViaDb($data)
	{
        /**
         * @var Model $db
         */
        $db = Model::init('LBS_PARSE');

		$data = $db->select(self::$table, '*', [
		    'lac' => $data['lac'],
            'ci' => $data['ci'],
        ]);

		if (empty($data)) return false;

		return $data[0];
	}


    /**
     * 将查询到的数据保存到数据库中
     * @param $data
     */
	protected static function saveAddressData($data)
    {
        /**
         * @var Model $db
         */
        //$redis = TbRedis::init('LBS_PARSE');
        $db = Model::init('LBS_PARSE');

        //$db->insert('lbs_address', $data);
        $db->insert(self::$table, $data);
    }

	/**
	 * 解析SystemMode, 由于system mode会携带 - , 所以需要过滤
	 * @param string $systemMode
     * @return string
	 */
	protected static function filterSystemMode($systemMode)
	{
		$arr = explode('-', $systemMode);

		if (count($arr) == 1) return strtoupper($arr[0]);

		return strtoupper($arr[0] . $arr[1]);
	}


	/**
	 * 获取MCC和MNC, 获取失败时返回false
	 * @param string $string
	 * @return array|bool
	 */
	protected function getMccAndMnc($string)
	{
		list($mcc, $mnc) = explode('-', $string);


		// mcc和mnc不在指定范围内
		if (!isset(self::MNC[$mnc]) || !isset(self::MCC[$mcc])) return false;
		
		return [
			'mcc' => $mcc,
			'mnc' => $mnc,
		];
	}

	/**
	 * 解析LTE
	 * @param array $arr 协议
	 * @return array
	 * arr => [
	 *    <System Mode>,
	 *    <Operation Mode>,
	 *    <MCC>-<MNC>, 国家代码 - 运行商代码
	 *    <TAC>,
	 *    <SCellID>,
	 *    <PCellID>,
	 *    <Frequency Band>,
	 *    <earfcn>,
	 *    <dlbw>,
	 *    <ulbw>,
	 *    <RSRQ>,
	 *    <RSRP>,
	 *    <RSSI>,
	 *    <RSSNR>
	 * ]
	 */
	public function parseLTE($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		$lac = base_convert($arr[3], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[4]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[4]
		];
	}

	/*<System Mode>,<Operation Mode>,<MCC>-<MNC>,<L
	AC>,<Cell ID>,<Absolute RF Ch Num>,<RxLev>,
	<Track LO Adjust>,<C1-C2>*/
	public function parseGSM($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		$lac = base_convert($arr[3], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[4]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[4]
		];
	}


    /*<System Mode>,<Operation Mode>,<MCC>-<MNC>,<
	LAC>,<Cell ID>,<Frequency Band>,<PSC>,<Freq>,<SSC>,<EC
	/IO>,<RSCP>,<Qual>,<RxLev>,<TXPWR>*/
	public function parseWCDMA($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		$lac = base_convert($arr[3], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[4]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[4]
		];
	}

	/*<System Mode>,<Operation Mode>,<MCC>-<MNC>,<
	LAC>,<Cell ID>,<Frequency Band>,<Uarfcn>,<Cpid>*/
	public function parseTDSCDMA($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		$lac = base_convert($arr[3], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[4]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[4]
		];
	}

	/*CDMA,<Operation Mode>[,<MCC>-<MNC>,<CDMA ch
	num>,<CDMA pilot PN>,<CDMA RX Chain 0 AGC>,<CDMA
	RX Chain 1 AGC>,<CDMA Chain 0 LNA>,<CDMA Chain 1
	LNA>,<CDMA TX AGC>,<SID>,<NID>,<CDMA
	EC/IO>,<BID>]*/
	// 取 SID(10 => CI), NID(11 => LAC)
	public function parseCDMA($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		$lac = base_convert($arr[11], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[10]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[10]
		];
	}

	/* EVDO,<Operation Mode>[,<MCC>-<MNC>,<EVDO ch
	num>,<EVDO RX Chain 0 AGC>,<EVDO RX Chain 1 AGC>,<
	EVDO TX AGC>,<EVDO Serving PN>,<EVDO Rel0
	SCI>,<EVDO RelA SCI>,<EVDO EC/IO>]*/
	public function parseEVDO($arr)
	{
		if (!$data = $this->getMccAndMnc($arr[2])) return false;

		// just for test
		$lac = base_convert($arr[3], 16, 10);

		// lac值转化失败 或者ci不存在
		if (!$lac || !$arr[4]) return false;
		return $data + [
			'lac' => $lac,
			'ci'  => $arr[4]
		];
	}


	protected static function getAddressByLBS($config)
	{
		$config['output'] = 'json';
	    $query = http_build_query($config);

	    $output = self::getCurlData('http://api.cellocation.com:81/cell/?' . $query);

	    return $output;
	}


	protected static function getCurlData($url)
	{
	    $ch = curl_init($url);

	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	    curl_setopt($ch, CURLOPT_HEADER, 0);
	    curl_setopt($ch, CURLOPT_ENCODING, "gzip");
	    $output = curl_exec($ch);
	    curl_close($ch);

	    return json_decode($output, true) ?: [];
	}
}

